package porter

import (
	"log"
	"shop/src/global"
	"shop/src/model"
	"shop/src/repository"
	"time"
)

func NewSynchronizer(
	orderType model.OrderType,
	storeId int64,
	cr *repository.CheckpointRepository,
	dr *repository.DocumentRepository,
) Synchronizer {
	store, err := getStore(storeId)
	if err != nil {
		return nil
	}
	if store == nil {
		log.Printf("门店信息[storeId:%d]不存在\n", storeId)
		return nil
	}
	platformId := store.PlatformId

	var f any
	if orderType == model.Trade {
		switch platformId {
		case "tb":
			f = &tbTradeFetcher{store: store}
		case "jd":
			f = &jdTradeFetcher{store: store}
		case "pdd":
			f = &pddTradeFetcher{store: store}
		case "ks":
			f = &ksTradeFetcher{store: store}
		}
	}
	if orderType == model.Refund {
		switch platformId {
		case "tb":
			f = &tbRefundFetcher{store: store}
		case "jd":
			f = &jdRefundFetcher{store: store}
		case "pdd":
			f = &pddRefundFetcher{store: store}
		case "ks":
			f = &ksRefundFetcher{store: store}
		}
	}

	if f == nil {
		log.Printf("门店[storeId:%d]的PlatformId[%s]不存在", storeId, platformId)
		return nil
	}

	switch f.(type) {
	case *PageFetcher:
		return &pageSynchronizer{
			store:   store,
			fetcher: f.(PageFetcher),
			cr:      cr,
			dr:      dr,
		}
	case *CursorFetcher:
		return &cursorSynchronizer{
			store:   store,
			fetcher: f.(CursorFetcher),
			cr:      cr,
			dr:      dr,
		}
	}
	log.Println("Fetcher not found, please check store config.")
	return nil // if happened, please check store config.
}

func getStore(storeId int64) (*model.Store, error) {
	var store model.Store
	_, err := global.Engine.ID(storeId).Get(&store)
	if err != nil {
		return nil, err
	}
	return &store, nil
}

type Synchronizer interface {
	FetchAndProcess()
}

type pageSynchronizer struct {
	orderType int

	store *model.Store

	fetcher PageFetcher

	cr *repository.CheckpointRepository

	dr *repository.DocumentRepository
}

func (s *pageSynchronizer) FetchAndProcess() {
	parameters := s.fetcher.parameters()
	if parameters == nil {
		parameters = []any{nil}
	}
	for _, parameter := range parameters {
		s.fetchAndProcess(parameter)
	}
}

func (s *pageSynchronizer) fetchAndProcess(parameter any) {
	startPage := s.fetcher.startPage()
	pageSize := s.fetcher.pageSize()
	startTime, endTime := s.getSyncTime()

	// get count
	response, err := s.fetcher.fetch(startTime, endTime, startPage, parameter, true)
	if err != nil {
		return
	}
	count := s.fetcher.count(response)
	page := count / pageSize
	if count%pageSize > 0 {
		page++
	}

	// get data and save data
	for pageNo := 0; pageNo < page; pageNo++ {
		pageNo = pageNo + startPage
		response, err := s.fetcher.fetch(startTime, endTime, pageNo, parameter, s.fetcher.hasTotal())
		if err != nil {
			return
		}
		// get data
		docs := s.fetcher.buildDocument(response)

		// save data
		for _, d := range docs {
			d.StoreId = s.store.Id
		}
		err = s.dr.Insert(docs)
		if err != nil {
			return
		}
	}

	err = s.cr.Update(s.store.Id, s.orderType)
	if err != nil {
		log.Printf("Update checkpoint error: %s", err.Error())
	}
}

func (s *pageSynchronizer) getSyncTime() (time.Time, time.Time) {
	checkpoint := s.getCheckpoint()
	return checkpoint.Time, time.Now()
}

func (s *pageSynchronizer) getCheckpoint() *model.Checkpoint {
	checkpoint, err := s.cr.GetByStoreId(s.store.Id, s.orderType)
	if err != nil {
		return nil
	}
	return checkpoint
}

type cursorSynchronizer struct {
	orderType int

	store *model.Store

	fetcher CursorFetcher

	cr *repository.CheckpointRepository

	dr *repository.DocumentRepository
}

func (s *cursorSynchronizer) FetchAndProcess() {
	parameters := s.fetcher.parameters()
	if parameters == nil {
		parameters = []any{nil}
	}
	for _, parameter := range parameters {
		s.fetchAndProcess(parameter)
	}
}

func (s *cursorSynchronizer) fetchAndProcess(parameter any) {
	pageSize := s.fetcher.pageSize()
	startTime, endTime := s.getSyncTime()

	response, err := s.fetcher.fetch(startTime, endTime, pageSize, parameter, nil)
	if err != nil {
		return
	}
	docs := s.fetcher.buildDocument(response)
	err = s.dr.Insert(docs)
	if err != nil {
		return
	}
	for s.fetcher.hasNext(response) {
		response, err = s.fetcher.fetch(startTime, endTime, pageSize, parameter, response)
		if err != nil {
			return
		}
		docs = s.fetcher.buildDocument(response)
		err = s.dr.Insert(docs)
		if err != nil {
			return
		}
	}
}

func (s *cursorSynchronizer) getSyncTime() (time.Time, time.Time) {
	checkpoint := s.getCheckpoint()
	return checkpoint.Time, time.Now()
}

func (s *cursorSynchronizer) getCheckpoint() *model.Checkpoint {
	checkpoint, err := s.cr.GetByStoreId(s.store.Id, s.orderType)
	if err != nil {
		return nil
	}
	return checkpoint
}

type PageFetcher interface {
	// 拉取订单数据
	fetch(startTime time.Time, endTime time.Time, pageNo int, parameter any, hasTotal bool) (any, error)

	// 响应是否需要包含数据总数
	hasTotal() bool

	// 获取当前Resp的订单总数
	count(resp any) int

	// 将Resp转换为Document
	buildDocument(resp any) []model.Document

	// 请求页大小
	pageSize() int

	// 请求开始页，0或1
	startPage() int

	// 扩展参数
	parameters() []any
}

type CursorFetcher interface {

	// 拉取订单数据
	fetch(startTime time.Time, endTime time.Time, pageSize int, parameter any, resp any) (any, error)

	// 判断当前Resp是否还有下一页
	hasNext(resp any) bool

	// 将Resp转换为Document
	buildDocument(resp any) []model.Document

	// 请求页大小
	pageSize() int

	// 扩展参数
	parameters() []any
}
